﻿module Sustainalytics.Module {

    export class Module {
        public files: string[];
        public require: string[];
    }


    export class ModuleLoaderConfig {

        private static count: number = 0;
        private static totalCount: number = 0;

        // application modules
        public static modules: any;


        // progress interval
        public static scriptLoadProgressTimeout: number = 0;

        // window setTimeout handle
        private static scriptLoadProgressTimeoutHandle: any = null;

        // called to indicate script loading progress
        public static scriptLoadProgressFunc: (count: number, totalCount: number) => void;


        public static addCount(count: number) {
            ModuleLoaderConfig.count += count;
            ModuleLoaderConfig.reportProgress();
        }

        public static removeCount(count: number) {
            ModuleLoaderConfig.count -= count;
            ModuleLoaderConfig.reportProgress();
        }

        public static addTotalCount(totalCount: number) {
            ModuleLoaderConfig.totalCount += totalCount;
            ModuleLoaderConfig.reportProgress();
        }

        public static removeTotalCount(count: number, totalCount: number) {
            ModuleLoaderConfig.count -= count;
            ModuleLoaderConfig.totalCount -= totalCount;
             ModuleLoaderConfig.reportProgress();
        }

        private static reportProgress() {
            if (!ModuleLoaderConfig.scriptLoadProgressTimeoutHandle && ModuleLoaderConfig.scriptLoadProgressFunc) {

                // report progress after timeout
                ModuleLoaderConfig.scriptLoadProgressTimeoutHandle = window.setTimeout(function () {

                    ModuleLoaderConfig.scriptLoadProgressFunc(ModuleLoaderConfig.count, ModuleLoaderConfig.totalCount);
                    ModuleLoaderConfig.scriptLoadProgressTimeoutHandle = null;

                }, ModuleLoaderConfig.scriptLoadProgressTimeout);
            }
        }
    }


    /**
        Loading AngularJS Components After Your Application Has Been Bootstrapped
        http://www.bennadel.com/blog/2553-loading-angularjs-components-after-your-application-has-been-bootstrapped.htm
    */
    export class ModularAngular {

        public static config(mod: ng.IModule, inputInlineAnnotatedFunc?: any[]): ng.IModule {

            // annotations valid for current custom config function
            var localAnnotations =
                [
                    '$controllerProvider',
                    '$provide',
                    '$compileProvider',
                    '$filterProvider'
                ];

            // annotations valid for entire config
            var annotations = [];
            annotations.push.apply(annotations, localAnnotations);

            var userFunc: any = null;

            if (inputInlineAnnotatedFunc && inputInlineAnnotatedFunc.length > 0) {

                // user defined function is the last in the annotation array
                userFunc = inputInlineAnnotatedFunc[inputInlineAnnotatedFunc.length - 1];

                // get arguments from the annotation array except the last element which is the function to call
                inputInlineAnnotatedFunc.forEach(function (inputArg, index) {
                    if (index < inputInlineAnnotatedFunc.length - 1) {

                        // add function annotation
                        annotations.push(inputArg);
                    }
                });
            }


            // configuration function to be called
            function configFunc($controllerProvider: ng.IControllerProvider, $provide: ng.IServiceProvider, $compileProvider: ng.ICompileProvider, $filterProvider: ng.IFilterProvider) {

                // keep old values
                mod['_controller'] = mod.controller;
                mod['_service'] = mod.service;
                mod['_factory'] = mod.factory;
                mod['_value'] = mod.value;
                mod['_directive'] = mod.directive;
                mod['_filter'] = mod.filter;


                // lazy controller registration
                (<any>mod)['controller'] = function (name, ctor) {
                    $controllerProvider.register(name, ctor);
                    return (this);
                };

                // lazy service registration
                (<any>mod)['service'] = function (name, ctor) {
                    $provide['service'](name, ctor);
                    return (this);
                };

                // lazy factory registration
                (<any>mod)['factory'] = function (name, factory) {
                    $provide['factory'](name, factory);
                    return (this);
                };

                // lazy value registration
                (<any>mod)['value'] = function (name, factory) {
                    $provide['value'](name, factory);
                    return (this);
                };

                // lazy directive registration
                (<any>mod)['directive'] = function (name, factory) {
                    $compileProvider.directive(name, factory);
                    return (this);
                };

                // lazy filter registration
                (<any>mod)['filter'] = function (name, factory) {
                    $filterProvider.register(name, factory);
                    return (this);
                };


                // call user defined func
                if (userFunc) {

                    // determine user function arguments
                    var userFuncArgs: any[] = <any[]><any>arguments;
                    userFuncArgs = userFuncArgs.slice(localAnnotations.length, userFuncArgs.length - 1);

                    // call user function
                    userFunc.apply(mod, userFuncArgs);
                }
            }


            // describe function arguments
            configFunc['$inject'] = annotations;


            // apply config function to current module
            return mod.config(configFunc);
        }
    }
}